//
// (C) Copyright 2006 Alexander Tsvyashchenko - http://www.ndl.kiev.ua
//
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef _FASTSIG_COMMON_HPP_
#define _FASTSIG_COMMON_HPP_

#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/noncopyable.hpp>

#include <list>

//-----------------------------------------------------------------------------
namespace fastsig
  {
  namespace detail
    {
    // Interface class for connection implementation
    class connection_impl_base
      {
      public:
        virtual void disconnect() = 0;
        virtual bool connected() const = 0;
      };

    template <class Signature>
    class signal_impl_common;
    }

  // connection interface for the user
  // non-constructable (constructed by signal),
  // copyable.
  class connection
    {
    template <class Signature>
    friend class detail::signal_impl_common;

    public:
                connection(){};
      void disconnect();
      bool connected() const;

    private:
      boost::shared_ptr<detail::connection_impl_base> m_connection;

    private:
      connection(boost::shared_ptr<detail::connection_impl_base> conn): m_connection(conn) {}
    };

  // Tracking support - to derive users types from
  class trackable
    {
    template <class Signature>
    friend class detail::signal_impl_common;

    protected:
      trackable() {}
      trackable(const trackable&) {}
      virtual ~trackable();
      // TODO: should we disconnect everything here?
      trackable& operator=(const trackable&) { return *this; }

    private:
      std::list<boost::shared_ptr<detail::connection_impl_base> > m_connections;

    private:
      void on_slot_connect(boost::shared_ptr<detail::connection_impl_base> conn)
        {
        m_connections.push_back(conn);
        }
    };

  namespace detail
    {
    // Forward declaration, actual declarations will
    // be specialized for different signatures
    template <class Signature>
    class slot_base;

    // Forward declaration, actual declarations will
    // be specialized for different signatures
    template <class slot_type, class Signature>
    class slot_impl;

    // Forward declaration, actual declarations will
    // be specialized for different signatures
    template <class Signature>
    class signal_impl;

    // Forward declaration for actual connections implementation
    template <class Signature>
    class connection_impl;

    // Signal implementation, excluding the actual call to the
    // slots, as it requires specialization with arguments.
    // Noncopyable to prevent unexpected calls from deeply copied objects.
    template <class Signature>
    class signal_impl_common: private boost::noncopyable
      {
      friend class connection_impl<Signature>;

      public:
        // For those who want the type slot_type, we provide
        // the typedef to boost::function; however, in fact
        // we work with anything that supports the required calling
        // syntax. Using "function" would be nicer, but at least
        // in some cases boost::function is slower and prevents
        // some optimizations, which are possible for "general"
        // form.
        typedef boost::function<typename boost::remove_pointer<Signature>::type> slot_type;

      public:
        template <class Slot>
        connection connect(const Slot& slot);

        // We do not provide "nice" tracking - only the explicit
        // way to specify one depending object.
        template <class Slot>
        connection connect(const Slot& slot, trackable& obj);

        // For sigc++ compartibility
        void clear();

        // For boost.signal compartibility
        void disconnect_all_slots();

        // No actual need for virtual desctructor here, it seems.
        ~signal_impl_common();

      protected:
        // TODO: we use 'new' in allocations and default allocator for list;
        // should we provide the way to specify allocators?
        typedef std::pair<slot_base<Signature>*, boost::shared_ptr<connection_impl_base> > slot_storage;
        typedef std::list<slot_storage> slots;

        mutable slots m_slots;

                //Bull's addition
                signal_impl_common()
                        :iterating(false),advanceIterator(&signal_impl_common::normalAdvance)
                {
                }
                mutable bool iterating;
                mutable typename slots::iterator it;
                mutable void(signal_impl_common::*advanceIterator)() const;
                void normalAdvance() const
                {
                        ++it;
                }
                void eraseAdvance() const
                {
                        delete it -> first;
                        it=m_slots.erase(it);
                        advanceIterator=&signal_impl_common::normalAdvance;//restore the normal behaviour
                }
               
      private:
        void disconnect(typename slots::iterator it);
      };

    template <class Signature>
    class connection_impl: public connection_impl_base
      {
      public:
        typedef typename signal_impl_common<Signature>::slots::iterator iterator;

      public:
        connection_impl(signal_impl_common<Signature>& signal, iterator it);
        virtual void disconnect();
        virtual bool connected() const;

      private:
        signal_impl_common<Signature>& m_signal;
        iterator m_it;
        bool m_connected;
      };
    } // namespace detail
  } // namespace fastsig
//-----------------------------------------------------------------------------
// Implementation
//-----------------------------------------------------------------------------
namespace fastsig
  {
  inline void connection::disconnect()
    {
    return m_connection -> disconnect();
    }

  inline bool connection::connected() const
    {
    return m_connection -> connected();
    }

  inline trackable::~trackable()
    {
    while (!m_connections.empty())
      {
      m_connections.back() -> disconnect();
      m_connections.pop_back();
      }
    }


  namespace detail
    {
    template <class Signature>
    template <class Slot>
    connection signal_impl_common<Signature>::connect(const Slot& slot)
      {
      std::auto_ptr<slot_base<Signature> > new_slot(new slot_impl<Slot, Signature>(slot));
      m_slots.push_back(std::make_pair(new_slot.get(), boost::shared_ptr<connection_impl<Signature> >()));
      new_slot.release();
      m_slots.back().second.reset(new connection_impl<Signature>(*this, --m_slots.end()));
      return connection(m_slots.back().second);
      }

    template <class Signature>
    template <class Slot>
    connection signal_impl_common<Signature>::connect(const Slot& slot, trackable& obj)
      {
      connection conn(connect(slot));
      obj.on_slot_connect(conn.m_connection);
      return conn;
      }

    template <class Signature>
    void signal_impl_common<Signature>::clear()
      {
      while (!m_slots.empty())
        m_slots.back().second -> disconnect();
      }

    template <class Signature>
    void signal_impl_common<Signature>::disconnect_all_slots()
      {
      clear();
      }

    template <class Signature>
    signal_impl_common<Signature>::~signal_impl_common()
      {
      clear();
      }

    template <class Signature>
    void signal_impl_common<Signature>::disconnect(typename slots::iterator it)
      {
                  if(iterating&&this->it==it)//make sure we are refering to the same slot
                  {
                          advanceIterator=&signal_impl_common::eraseAdvance;
                  }
                  else
                  {
                          delete it -> first;
                          m_slots.erase(it);
                  }
      }

    template <class Signature>
    connection_impl<Signature>::connection_impl(signal_impl_common<Signature>& signal, iterator it):
      m_signal(signal), m_it(it), m_connected(true) {}

    template <class Signature>
    void connection_impl<Signature>::disconnect()
      {
      if (m_connected)
        {
        m_connected = false;
        m_signal.disconnect(m_it);
        }
      }

    template <class Signature>
    bool connection_impl<Signature>::connected() const
      {
      return m_connected;
      }
    }
  } // namespace fastsig
//-----------------------------------------------------------------------------
#endif